/*
 * Part of the ACID Project
 * Developed in 2011 by Morgan Taschuk
 */
package acid.plugin.expression.similarity;

import cern.colt.matrix.impl.SparseDoubleMatrix2D;
import cytoscape.Cytoscape;
import cytoscape.data.CyAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 *
 * @author morgan
 */
public class CalculateGlobalSimilarity
{

    private SparseDoubleMatrix2D expressionSimiliarities;
    private ArrayList<String> nodes;
    private List<Integer[]> unknownValues;
    private List<Double> expressionValues;
    private double mean;

    /**
     * Calculates the pairwise similarity of all genes in the
     * current network.
     * @param nodes
     */
    public CalculateGlobalSimilarity(ArrayList<String> nodes)
    {
        this.nodes = nodes;
        unknownValues = new ArrayList<Integer[]>();
        expressionValues = new ArrayList<Double>();
        CyAttributes attributes = Cytoscape.getNodeAttributes();


        expressionSimiliarities = new SparseDoubleMatrix2D(nodes.size(), nodes.size());
        for (int i = 0; i < nodes.size(); i++)
        {
            Map<String, Double> vals1 = attributes.getMapAttribute(nodes.get(i),
                    "expression_vals");
            for (int j = i + 1; j < nodes.size(); j++)
            {
                Map<String, Double> vals2 = attributes.getMapAttribute(nodes.get(j),
                        "expression_vals");
                if (vals1 == null || vals2 == null)
                {
                    unknownValues.add(new Integer[]{i, j});
                    continue;
                }
                if (calculateSimilarity(i, j, vals1, vals2))
                if (expressionSimiliarities.getQuick(i, j)==0 &&
                        expressionSimiliarities.getQuick(j, i)==0)
                {
                    System.out.println("Problem with "+ nodes.get(i)+
                            " and "+nodes.get(j));
                }
            }
        }

        double[] simVals = CalculatePairwiseSimilarity.toDoubleArray(expressionValues);
        mean = CalculatePairwiseSimilarity.calculateMean(simVals);

        //for all unknowns, set to the mean
        for (Integer[] i : unknownValues)
        {
            expressionSimiliarities.setQuick(i[0], i[1], mean);
        }


        for (int i =0; i< nodes.size(); i++)
        {
            for (int j = i+1; j<nodes.size(); j++)
            {
                double d = expressionSimiliarities.getQuick(i, j);
                if (d==0.0)
                {
                    d = expressionSimiliarities.getQuick(j, i);
                }
                String val = new Double(d).toString();
                //System.out.print((val.length() > 5 ? val.substring(0, 5): val)+"\t");
            }
            //System.out.println();
        }



    }

    private boolean calculateSimilarity(int i, int j, Map<String, Double> vals1, Map<String, Double> vals2)
    {
        ArrayList<Double> one = new ArrayList<Double>();
        ArrayList<Double> two = new ArrayList<Double>();

        //only add experiments in which both genes feature
        for (String s : vals1.keySet())
        {
            if (vals2.containsKey(s))
            {
                one.add(vals1.get(s));
                two.add(vals2.get(s));
            }
        }

        //if the genes have expresssion data but no overlapping experiments
        if (one.isEmpty() || two.isEmpty() || one.size()==1)
        {
            unknownValues.add(new Integer[]{i, j});
            return false;
        }

        double[] log1 = CalculatePairwiseSimilarity.toDoubleArray(one);
        double[] log2 = CalculatePairwiseSimilarity.toDoubleArray(two);

        //calculate the similarity
        Double sim = CalculatePairwiseSimilarity.calculateZScorePearson(log1, log2);
        if (sim.isNaN())
        {
            //printArray(log1);
            //printArray(log2);
            unknownValues.add(new Integer[]{i, j});
            return false;
        }

        expressionSimiliarities.setQuick(i, j, sim);
        expressionValues.add(sim);
        return true;
    }


    private void printArray(double[] log)
    {
        System.out.print("array: ");
        for (double d: log)
        {
            System.out.print(d+" ");
        }
        System.out.println();
    }


    /**
     * Gets the co-expression value of the given identifiers.
     * @param a Cytoscape identifier
     * @param b Cytoscape identifier
     * @return
     */
    public double getCoExpressionValue(String a, String b)
    {
        int index1 = nodes.indexOf(a);
        int index2 = nodes.indexOf(b);
        double result = expressionSimiliarities.get(index1, index2);
        if (result==0)
        {
            result = expressionSimiliarities.get(index2, index1);
        }
        return result;
    }

    /**
     * Sums the expression values in the path.
     * @param path the Cytoscape indices for the path
     * @return
     */
    public double pathSimilarity(ArrayList<String> path)
    {
        double total=0;

        //sums the expression values
        for (int i=0; i<path.size();i++)
        {
            String a = path.get(i);
            for (int j=i+1; j<path.size();j++)
            {
                double cox =getCoExpressionValue(a, path.get(j));
                //System.out.println("\t"+path.get(i)+" - "+path.get(j)+" = "+cox);
                total += cox;
            }
        }
        
        return total/combination(path.size(),2);
    }

    private int factorial(int num)
    {
        int total = 1;
        while (num > 1)
        {
            total *= num--;
        }
        return total;
    }

    private int combination(int n, int k)
    {
        int result = factorial(n)/(factorial(k)*factorial(n-k));
        return result;
    }


}
